package com.demo.shardingsphere5.aop;

import com.demo.shardingsphere5.annotation.ShardingStrategy;
import com.demo.shardingsphere5.config.ShardingConstantConfig;
import com.demo.starter.aop.AopUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shardingsphere.infra.hint.HintManager;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;
import java.util.zip.CRC32;

/**
 * 分片注解aop
 * @author K
 */
@Slf4j
@Aspect
@Configuration
public class ShardingStrategyAop {

    @Resource
    private ShardingConstantConfig shardingConstantConfig;

    @Pointcut("@annotation(com.demo.shardingsphere5.annotation.ShardingStrategy)")
    public void addAdvice() {}

    @Around("addAdvice()")
    public Object interceptor(ProceedingJoinPoint pjp) throws Throwable {
        Method method = AopUtil.getMethod(pjp);
        ShardingStrategy shardingStrategy = method.getAnnotation(ShardingStrategy.class);
        if (StringUtils.isAllBlank(shardingStrategy.shardingValue(), shardingStrategy.dbIndex())) {
            throw new IllegalArgumentException("ShardingStrategy注解shardingColumn和dbIndex不能同时为空");
        }

        String databaseName;
        if (StringUtils.isNotBlank(shardingStrategy.shardingValue())) {
            Object value = AopUtil.springElParser(shardingStrategy.shardingValue(), pjp);

            long mod = 0;
            if (value instanceof String) {
                CRC32 crc32 = new CRC32();
                crc32.update(value.toString().getBytes());
                mod = crc32.getValue();
            } else if (value instanceof Integer || value instanceof Long) {
                mod = ((Number) value).longValue() % 2;
            }

            databaseName = shardingConstantConfig.getName() + mod % shardingConstantConfig.getNum();
            log.info("shardingColumn分片值：{}，数据库：{}", value, databaseName);
        } else {
            Object dbIndex = AopUtil.springElParser(shardingStrategy.dbIndex(), pjp);
            databaseName = shardingConstantConfig.getName() + dbIndex;
            log.info("dbIndex分片值：{}，数据库：{}", dbIndex, databaseName);
        }

        if (StringUtils.isBlank(databaseName)) {
            throw new IllegalArgumentException("ShardingStrategy注解属性匹配不到入参参数");
        }

        try (HintManager instance = HintManager.getInstance()) {
            instance.setDatabaseShardingValue(databaseName);
            return pjp.proceed();
        }
    }
}
